package com.tangula.web.config.auth;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;

import javax.servlet.Filter;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.web.bind.annotation.RequestMapping;

import com.tangula.auth.TangulaAuthentication;
import com.tangula.auth.TangulaPasswordEncoder;
import com.tangula.auth.TangulaUserDetailsService;
import com.tangula.core.Controller;

/**
 * 应用安全配置.
 * 
 * @version v0.0.1 by wangds 2018-07-22 完善Filter，AuthProvider的配置.
 *
 * @author wangds &lt;wangds2016@163.com&gt;
 */
// @EnableGlobalAuthentication
@EnableWebSecurity
public class AppSecurityConfig extends WebSecurityConfigurerAdapter {

	
	@Autowired
	ApplicationContext ctx;
	
	@Autowired
	protected TangulaUserDetailsService usersService;

	@Autowired
	protected TangulaPasswordEncoder passwordEncoder;

	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		UserDetailsService uds = ctx.getBean(TangulaUserDetailsService.class);
		auth.userDetailsService(uds).passwordEncoder(passwordEncoder);
	}
	
	@Override
	public void configure(WebSecurity web) throws Exception {
		super.configure(web);
		
		configWebIgnorePaths(web);
		
	}

	@SuppressWarnings("rawtypes")
	private void configWebIgnorePaths(WebSecurity web) {
		Set<String> ignore_path_set = new TreeSet<>();
		
		Map<String, Controller> controllers = ctx.getBeansOfType(Controller.class);
		for(Entry<String, Controller> entry: controllers.entrySet()) {
			Controller ctrl = entry.getValue();
			Class<? extends Controller> clz = ctrl.getClass();
			RequestMapping anns_clz = clz.getAnnotation(RequestMapping.class);
			String[] base_urls = null;
			if(anns_clz!=null) {
				base_urls = anns_clz.value();
			}else {
				continue;
			}
			Ignore ann_clz_ignore = clz.getAnnotation(Ignore.class);
			boolean clz_ignore = ann_clz_ignore!=null;
			
			for(Method method:clz.getDeclaredMethods()) {
				Ignore method_ann_ignore = method.getAnnotation(Ignore.class);
				if(method_ann_ignore==null&&!clz_ignore) {
					continue;
				}
				
				RequestMapping req_method_ann = method.getAnnotation(RequestMapping.class);
				if(req_method_ann==null) {
					continue;
				}
				
				String[] sub_uris = req_method_ann.value();
				
				StringBuffer sb = new StringBuffer();
				for(String sub: sub_uris) {
					for(String base: base_urls) {
						sb.append(base).append(sub);
						ignore_path_set.add(sb.toString());
						sb.delete(0, sb.length());
					}
				}
				
			}
			
			
		}
		
		String[] ignore_paths = new String[ignore_path_set.size()];
		ignore_path_set.toArray(ignore_paths);
		web.ignoring().antMatchers(ignore_paths);
	}

	@Override
	protected void configure(HttpSecurity http) throws Exception {

		http.httpBasic()
        .and()
        .csrf().disable()
        .authorizeRequests()
        .anyRequest().authenticated()
        ;
		
		configAuthProviders(http);
		configAuthFilters(http);
		
	}
	private void configAuthProviders(HttpSecurity http) {
		Map<String, TangulaAuthentication> providers = ctx.getBeansOfType(TangulaAuthentication.class);
		if(providers==null) {
			return;
		}
		for(Entry<String, TangulaAuthentication> entry: providers.entrySet()) {
			if(entry==null) {
				continue;
			}
			TangulaAuthentication provider = entry.getValue();
			http.authenticationProvider(provider);
		}
	}

	private void configAuthFilters(HttpSecurity http) {
		Map<String, TangularAuthFilter> filters = ctx.getBeansOfType(TangularAuthFilter.class);
		if(filters==null) {
			return;
		}
		for(Entry<String, TangularAuthFilter> entry: filters.entrySet()) {
			if(entry==null) {
				continue;
			}
			Filter filter = entry.getValue();
			http.addFilter(filter);
		}
	}

	@Bean
	@Override
	protected UserDetailsService userDetailsService() {
		return usersService;
	}
	
}
